package zerrors

import (
	"fmt"
	"net/http"
	"sync"
)

var unknownCoder ErrCode = ErrCode{http.StatusInternalServerError, 1, "An internel server error occured.", ""}

type Coder interface {
	HTTPStatus() int
	Code() int
	String() string
	Reference() string
}

type ErrCode struct {
	HTTP int
	C    int
	Ext  string
	Ref  string
}

func (c ErrCode) HTTPStatus() int {
	if c.HTTP == 0 {
		return 500
	}
	return c.HTTP
}

func (c ErrCode) Code() int {
	return c.C
}

func (c ErrCode) String() string {
	return c.Ext
}

func (c ErrCode) Reference() string {
	return c.Ref
}

// 全局错误码和 coder 的映射
var codes = map[int]Coder{}
var codeMu = &sync.Mutex{}

func Register(coder Coder) {
	if coder.Code() == 0 {
		panic("code `0` is reserved as unknownCode")
	}

	codeMu.Lock()
	defer codeMu.Unlock()

	codes[coder.Code()] = coder
}

func MustRegister(coder Coder) {
	if coder.Code() == 0 {
		panic("code `0` is reserved as unknownCode")
	}

	codeMu.Lock()
	defer codeMu.Unlock()

	if _, ok := codes[coder.Code()]; ok {
		panic(fmt.Sprintf("code: %d already exist.", coder.Code()))
	}

	codes[coder.Code()] = coder
}

// 提取错误中的 coder
func ParseCoder(err error) Coder {
	if err == nil {
		return nil
	}

	if v, ok := err.(*withCode); ok {
		if coder, ok := codes[v.code]; ok {
			return coder
		}
	}

	return unknownCoder
}

// 检查错误中的 coder.code 是否为指定值
func IsCode(err error, code int) bool {
	if v, ok := err.(*withCode); ok {
		if v.code == code {
			return true
		}

		if v.cause != nil {
			return IsCode(v.cause, code)
		}

		return false
	}

	return false
}

func init() {
	codes[unknownCoder.Code()] = unknownCoder
}
